<script setup lang="ts">
import type { HTMLAttributes } from "vue";
import { cn } from "@/utils";
import {
  Sheet,
  SheetContent,
  SheetDescription,
  SheetFooter,
  SheetHeader,
  SheetTitle,
} from "./sheet";

defineOptions({
  name: "FaDrawer",
});

const props = withDefaults(
  defineProps<{
    modelValue?: boolean;
    side?: "top" | "bottom" | "left" | "right";
    title: string;
    description?: string;
    loading?: boolean;
    closable?: boolean;
    centered?: boolean;
    bordered?: boolean;
    overlay?: boolean;
    overlayBlur?: boolean;
    showConfirmButton?: boolean;
    showCancelButton?: boolean;
    confirmButtonText?: string;
    cancelButtonText?: string;
    confirmButtonDisabled?: boolean;
    confirmButtonLoading?: boolean;
    header?: boolean;
    footer?: boolean;
    closeOnClickOverlay?: boolean;
    closeOnPressEscape?: boolean;
    destroyOnClose?: boolean;
    contentClass?: HTMLAttributes["class"];
    headerClass?: HTMLAttributes["class"];
    footerClass?: HTMLAttributes["class"];
  }>(),
  {
    modelValue: false,
    side: "right",
    loading: false,
    closable: true,
    centered: false,
    bordered: true,
    overlay: true,
    overlayBlur: false,
    showConfirmButton: true,
    showCancelButton: false,
    confirmButtonText: "确定",
    cancelButtonText: "取消",
    confirmButtonDisabled: false,
    confirmButtonLoading: false,
    header: true,
    footer: true,
    closeOnClickOverlay: true,
    closeOnPressEscape: true,
    destroyOnClose: true,
  },
);

const emits = defineEmits<{
  "update:modelValue": [value: boolean];
  open: [];
  opened: [];
  close: [];
  closed: [];
  confirm: [];
  cancel: [];
}>();

const id = useId();
provide("DrawerId", id);

const isOpen = ref(props.modelValue);

watch(
  () => props.modelValue,
  (newValue) => {
    isOpen.value = newValue;
  },
);

const hasOpened = ref(false);
const isClosed = ref(true);

watch(
  () => isOpen.value,
  (value) => {
    isClosed.value = false;
    if (value && !hasOpened.value) {
      hasOpened.value = true;
    }
  },
  {
    immediate: true,
  },
);

const forceMount = computed(() => !props.destroyOnClose && hasOpened.value);

function updateOpen(value: boolean) {
  isOpen.value = value;
  emits("update:modelValue", value);
  if (value) {
    emits("open");
  } else {
    emits("close");
  }
}

function onConfirm() {
  updateOpen(false);
  emits("confirm");
}

function onCancel() {
  updateOpen(false);
  emits("cancel");
}

function handleFocusOutside(e: Event) {
  e.preventDefault();
  e.stopPropagation();
}

function handleClickOutside(e: Event) {
  if (
    !props.closeOnClickOverlay ||
    (e.target as HTMLElement).dataset.drawerId !== id
  ) {
    e.preventDefault();
    e.stopPropagation();
  }
}

function handleEscapeKeyDown(e: KeyboardEvent) {
  if (!props.closeOnPressEscape) {
    e.preventDefault();
    e.stopPropagation();
  }
}

function handleAnimationEnd() {
  if (isOpen.value) {
    emits("opened");
  } else {
    emits("closed");
    isClosed.value = true;
  }
}
</script>

<template>
  <Sheet :modal="false" :open="isOpen" @update:open="updateOpen">
    <SheetContent
      :closable="props.closable"
      :open="isOpen"
      :overlay="props.overlay"
      :overlay-blur="props.overlayBlur"
      :class="
        cn('w-full flex flex-col gap-0 p-0', props.contentClass, {
          hidden: isClosed,
        })
      "
      :side="props.side"
      :force-mount="forceMount"
      @open-auto-focus="handleFocusOutside"
      @close-auto-focus="handleFocusOutside"
      @focus-outside="handleFocusOutside"
      @pointer-down-outside="handleClickOutside"
      @interact-outside="handleClickOutside"
      @escape-key-down="handleEscapeKeyDown"
      @animation-end="handleAnimationEnd"
    >
      <SheetHeader
        v-if="header"
        :class="
          cn('p-4 gap-y-1', props.headerClass, {
            'border-b': props.bordered,
          })
        "
      >
        <slot name="header">
          <SheetTitle :class="{ 'text-center': props.centered }">
            {{ title }}
          </SheetTitle>
          <SheetDescription
            class="empty:hidden"
            :class="{ 'text-center': props.centered }"
          >
            {{ description }}
          </SheetDescription>
        </slot>
      </SheetHeader>
      <div class="m-0 flex-1 of-y-hidden">
        <FaScrollArea class="h-full">
          <div class="p-4">
            <slot />
          </div>
        </FaScrollArea>
        <div
          v-show="props.loading"
          class="absolute inset-0 z-1000 size-full flex-center bg-popover/75"
        >
          <FaIcon name="i-line-md:loading-twotone-loop" class="size-10" />
        </div>
      </div>
      <SheetFooter
        v-if="footer"
        :class="
          cn('p-2 gap-y-2', props.footerClass, {
            'sm:justify-center': props.centered,
            'border-t': props.bordered,
          })
        "
      >
        <slot name="footer">
          <FaButton v-if="showCancelButton" variant="outline" @click="onCancel">
            {{ cancelButtonText }}
          </FaButton>
          <FaButton
            v-if="showConfirmButton"
            :disabled="confirmButtonDisabled"
            :loading="confirmButtonLoading"
            @click="onConfirm"
          >
            {{ confirmButtonText }}
          </FaButton>
        </slot>
      </SheetFooter>
    </SheetContent>
  </Sheet>
</template>
